package com.quanxiaoha.weblog.web.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.quanxiaoha.weblog.common.domain.dos.BlogSettingsDO;
import com.quanxiaoha.weblog.common.domain.dos.CommentDO;
import com.quanxiaoha.weblog.common.domain.dos.UserDO;
import com.quanxiaoha.weblog.common.domain.mapper.BlogSettingsMapper;
import com.quanxiaoha.weblog.common.domain.mapper.CommentMapper;
import com.quanxiaoha.weblog.common.domain.mapper.UserMapper;
import com.quanxiaoha.weblog.common.enums.CommentStatusEnum;
import com.quanxiaoha.weblog.common.enums.CommentTypeEnum;
import com.quanxiaoha.weblog.common.enums.ResponseCodeEnum;
import com.quanxiaoha.weblog.common.exception.BizException;
import com.quanxiaoha.weblog.common.utils.Response;
import com.quanxiaoha.weblog.web.convert.CommentConvert;
import com.quanxiaoha.weblog.web.event.PublishCommentEvent;
import com.quanxiaoha.weblog.web.model.vo.comment.*;
import com.quanxiaoha.weblog.web.service.CommentService;
import com.quanxiaoha.weblog.web.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import toolgood.words.IllegalWordsSearch;
import toolgood.words.IllegalWordsSearchResult;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: 犬小哈
 * @url: www.quanxiaoha.com
 * @date: 2023-09-15 14:03
 * @description: 知识库
 **/
@Service
@Slf4j
public class CommentServiceImpl implements CommentService {

    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private BlogSettingsMapper blogSettingsMapper;
    @Autowired
    private CommentMapper commentMapper;
    @Autowired
    private IllegalWordsSearch wordsSearch;
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    @Autowired
    private UserMapper userMapper;

    @Override
    public Response findQQUserInfo(FindQQUserInfoReqVO findQQUserInfoReqVO) {
        String qq = findQQUserInfoReqVO.getQq();

        // 校验 QQ 号
        if (!StringUtil.isPureNumber(qq)) {
            log.warn("昵称输入的格式不是 QQ 号: {}", qq);
            throw new BizException(ResponseCodeEnum.NOT_QQ_NUMBER);
        }

        // 直接基于 QQ 号返回头像与邮箱，避免依赖需要登录的QQ空间接口
        String avatarUrl = String.format("https://q.qlogo.cn/headimg_dl?dst_uin=%s&spec=640", qq);
        String nickname = qq; // 昵称回退为 QQ 号
        String mail = String.format("%s@qq.com", qq);

        return Response.success(FindQQUserInfoRspVO.builder()
                .avatar(avatarUrl)
                .nickname(nickname)
                .mail(mail)
                .build());
    }

    /**
     * 发布评论
     *
     * @param publishCommentReqVO
     * @return
     */
    @Override
    public Response publishComment(PublishCommentReqVO publishCommentReqVO) {
        // 回复的评论 ID
        Long replyCommentId = publishCommentReqVO.getReplyCommentId();
        // 评论内容
        String content = publishCommentReqVO.getContent();
        // 昵称
        String nickname = publishCommentReqVO.getNickname();

        // 查询博客设置相关信息（约定的 ID 为 1）
        BlogSettingsDO blogSettingsDO = blogSettingsMapper.selectById(1L);
        // 是否开启了敏感词过滤
        boolean isCommentSensiWordOpen = blogSettingsDO.getIsCommentSensiWordOpen();
        // 是否开启了审核
        boolean isCommentExamineOpen = blogSettingsDO.getIsCommentExamineOpen();

        // 设置默认状态（正常）
        Integer status = CommentStatusEnum.NORMAL.getCode();
        // 审核不通过原因
        String reason = "";

        // 如果开启了审核, 设置状态为待审核，等待博主后台审核通过
        if (isCommentExamineOpen) {
            status = CommentStatusEnum.WAIT_EXAMINE.getCode();
        }

        // 评论内容是否包含敏感词
        boolean isContainSensitiveWord = false;
        // 是否开启了敏感词过滤
        if (isCommentSensiWordOpen) {
            // 校验评论中是否包含敏感词
            isContainSensitiveWord = wordsSearch.ContainsAny(content);

            if (isContainSensitiveWord) {
                // 若包含敏感词，设置状态为审核不通过
                status = CommentStatusEnum.EXAMINE_FAILED.getCode();
                // 匹配到的所有敏感词组
                List<IllegalWordsSearchResult> results = wordsSearch.FindAll(content);
                List<String> keywords = results.stream().map(result -> result.Keyword).collect(Collectors.toList());
                // 不同过的原因
                reason = String.format("系统自动拦截，包含敏感词：%s", keywords);
                log.warn("此评论内容中包含敏感词: {}, content: {}", keywords, content);
            }
        }

        CommentDO commentDO = CommentDO.builder()
                .avatar(publishCommentReqVO.getAvatar())
                .content(content)
                .mail(publishCommentReqVO.getMail())
                .createTime(LocalDateTime.now())
                .nickname(nickname)
                .routerUrl(publishCommentReqVO.getRouterUrl())
                .website(publishCommentReqVO.getWebsite())
                .replyCommentId(replyCommentId)
                .parentCommentId(publishCommentReqVO.getParentCommentId())
                .status(status)
                .reason(reason)
                .build();

        commentMapper.insert(commentDO);

        Long commentId = commentDO.getId();

        // 发送评论发布事件
        eventPublisher.publishEvent(new PublishCommentEvent(this, commentId));

        // 给予前端对应的提示信息
        if (isContainSensitiveWord)
            throw new BizException(ResponseCodeEnum.COMMENT_CONTAIN_SENSITIVE_WORD);

        if (Objects.equals(status, CommentStatusEnum.WAIT_EXAMINE.getCode()))
            throw new BizException(ResponseCodeEnum.COMMENT_WAIT_EXAMINE);

        return Response.success();
    }

    /**
     * 查询页面所有评论
     *
     * @param findCommentListReqVO
     * @return
     */
    @Override
    public Response findCommentList(FindCommentListReqVO findCommentListReqVO) {
        // 路由地址
        String routerUrl = findCommentListReqVO.getRouterUrl();

        // 根据该路由地址下所有评论（仅查询状态正常的）
        List<CommentDO> commentDOS = commentMapper.selectByRouterUrlAndStatus(routerUrl, CommentStatusEnum.NORMAL.getCode());
        // 总评论数
        Integer total = commentDOS.size();

        List<FindCommentItemRspVO> vos = null;
        // DO 转 VO
        if (!CollectionUtils.isEmpty(commentDOS)) {
            // 一级评论
            vos = commentDOS.stream()
                    .filter(commentDO -> Objects.isNull(commentDO.getParentCommentId())) // parentCommentId 父级 ID 为空，则表示为一级评论
                    .map(commentDO -> convertCommentWithLatestUserInfo(commentDO))
                    .collect(Collectors.toList());

            // 循环设置评论回复数据
            vos.forEach(vo -> {
                Long commentId = vo.getId();
                List<FindCommentItemRspVO> childComments = commentDOS.stream()
                        .filter(commentDO -> Objects.equals(commentDO.getParentCommentId(), commentId)) // 过滤出一级评论下所有子评论
                        .sorted(Comparator.comparing(CommentDO::getCreateTime)) // 按发布时间升序排列
                        .map(commentDO -> {
                            FindCommentItemRspVO findPageCommentRspVO = convertCommentWithLatestUserInfo(commentDO);
                            Long replyCommentId = commentDO.getReplyCommentId();
                            // 若二级评论的 replayCommentId 不等于一级评论 ID, 前端则需要展示【回复 @ xxx】，需要设置回复昵称
                            if (!Objects.equals(replyCommentId, commentId)) {
                                // 设置回复用户的昵称
                                Optional<CommentDO> optionalCommentDO = commentDOS.stream()
                                        .filter(commentDO1 -> Objects.equals(commentDO1.getId(), replyCommentId)).findFirst();
                                if (optionalCommentDO.isPresent()) {
                                    CommentDO replyComment = optionalCommentDO.get();
                                    String replyNickname = getLatestNickname(replyComment);
                                    findPageCommentRspVO.setReplyNickname(replyNickname);
                                }
                            }
                            return findPageCommentRspVO;
                        }).collect(Collectors.toList());

                vo.setChildComments(childComments);
            });
        }

        return Response.success(FindCommentListRspVO.builder()
                .total(total)
                .comments(vos)
                .build());
    }

    /**
     * 转换评论为VO，并获取最新的用户信息
     * 
     * @param commentDO 评论DO
     * @return 评论VO
     */
    private FindCommentItemRspVO convertCommentWithLatestUserInfo(CommentDO commentDO) {
        FindCommentItemRspVO vo = CommentConvert.INSTANCE.convertDO2VO(commentDO);
        
        UserDO userDO = null;
        
        // 第一优先级：通过用户ID获取用户信息（注册用户评论）
        if (Objects.nonNull(commentDO.getUserId()) && 
            Objects.equals(commentDO.getCommentType(), CommentTypeEnum.REGISTERED_USER.getValue())) {
            userDO = userMapper.selectById(commentDO.getUserId());
        }
        
        // 第二优先级：通过邮箱匹配用户（兼容现有评论）
        if (Objects.isNull(userDO) && Objects.nonNull(commentDO.getMail()) && 
            !commentDO.getMail().trim().isEmpty() && commentDO.getMail().contains("@")) {
            
            // 通过邮箱查找用户
            userDO = userMapper.findByEmail(commentDO.getMail());
        }
        
        // 如果找到了用户，使用最新的用户信息
        if (Objects.nonNull(userDO)) {
            // 使用用户表中的最新昵称和头像
            String latestNickname = (Objects.nonNull(userDO.getNickname()) && !userDO.getNickname().trim().isEmpty()) 
                    ? userDO.getNickname() 
                    : userDO.getUsername();
            String latestAvatar = (Objects.nonNull(userDO.getAvatar()) && !userDO.getAvatar().trim().isEmpty()) 
                    ? userDO.getAvatar() 
                    : commentDO.getAvatar(); // 如果用户没有设置头像，使用评论时的头像
            
            vo.setNickname(latestNickname);
            vo.setAvatar(latestAvatar);
        }
        
        return vo;
    }

    /**
     * 获取评论的最新昵称
     * 
     * @param commentDO 评论DO
     * @return 最新昵称
     */
    private String getLatestNickname(CommentDO commentDO) {
        UserDO userDO = null;
        
        // 第一优先级：通过用户ID获取用户信息
        if (Objects.nonNull(commentDO.getUserId()) && 
            Objects.equals(commentDO.getCommentType(), CommentTypeEnum.REGISTERED_USER.getValue())) {
            userDO = userMapper.selectById(commentDO.getUserId());
        }
        
        // 第二优先级：通过邮箱匹配用户
        if (Objects.isNull(userDO) && Objects.nonNull(commentDO.getMail()) && 
            !commentDO.getMail().trim().isEmpty() && commentDO.getMail().contains("@")) {
            userDO = userMapper.findByEmail(commentDO.getMail());
        }
        
        // 如果找到用户，返回最新昵称
        if (Objects.nonNull(userDO)) {
            return (Objects.nonNull(userDO.getNickname()) && !userDO.getNickname().trim().isEmpty()) 
                    ? userDO.getNickname() 
                    : userDO.getUsername();
        }
        
        // 匿名评论或用户不存在时，返回评论表中的昵称
        return commentDO.getNickname();
    }

}
